#region using

using System;
using System.IO;
#if !SANDBOX
using System.Reflection;
#endif
using System.Text;
using System.Xml.Serialization;
using PS.Practices.SiteBuilder.Features.Schema.DOM;

#endregion

namespace PS.Practices.SiteBuilder.Features.Schema
{
    public class DeSerializer<RootClass>
    {
#if !SANDBOX
        private static void AddCompositeOverides<Container, BaseType>(Assembly[] assemblies, XmlAttributeOverrides overrides)
        {
            Type baseType = typeof (BaseType);
            Type container = typeof (Container);

            XmlAttributes attributes = new XmlAttributes();

            foreach (Assembly assembly in assemblies)
            {
                foreach (Type type in assembly.GetExportedTypes())
                {
                    if (type.IsClass && !type.IsAbstract && baseType.IsAssignableFrom(type))
                    {
                        attributes.XmlElements.Add(new XmlElementAttribute(type));
                    }
                }
            }
            overrides.Add(container, "Operations", attributes);
        }

        private static void AddCompositeOverides<Container, BaseType>(Assembly[] assemblies, Extension[] exs, XmlAttributeOverrides overrides)
        {
            Type baseType = typeof(BaseType);
            Type container = typeof(Container);

            XmlAttributes attributes = new XmlAttributes();

            foreach (Assembly assembly in assemblies)
            {
                foreach (Type type in assembly.GetExportedTypes())
                {
                    if (type.IsClass && !type.IsAbstract && baseType.IsAssignableFrom(type))
                    {
                        attributes.XmlElements.Add(new XmlElementAttribute(type));
                    }
                }
            }

            foreach (Extension ex in exs)
            {
                try
                {
                    Assembly assembly = Assembly.Load(new AssemblyName(ex.Assembly));
                    if(assembly != null)
                    {
                        foreach (Type type in assembly.GetExportedTypes())
                        {
                            if (type.IsClass && !type.IsAbstract && baseType.IsAssignableFrom(type))
                            {
                                XmlElementAttribute el = new XmlElementAttribute(type);
                                el.Namespace = ex.TagPrefix;
                                attributes.XmlElements.Add(el);
                            }
                        }
                    }
                }
                catch(Exception)
                {}
            }

            overrides.Add(container, "Operations", attributes);
        }
#endif

        private static XmlAttributeOverrides CreateXmlOverrides()
        {
#if !SANDBOX
            XmlAttributeOverrides overrides = new XmlAttributeOverrides();

            // TODO : load all plugin assemblies here
            Assembly[] assemblies = new[] {typeof (RootClass).Assembly};

            AddCompositeOverides<List, ListOperation>(assemblies, overrides);
            AddCompositeOverides<ProvisionerRoot, WebOperation>(assemblies, overrides);
            AddCompositeOverides<FixupWebACL, WebACLAction>(assemblies, overrides);
            AddCompositeOverides<WebPart, WebpartOperation>(assemblies, overrides);
            AddCompositeOverides<Web.ActionContainer, WebOperation>(assemblies, overrides);
            return overrides;
#else
            throw new Exception("'CreateXmlOverrides' method cannot be used in sandbox solution");
#endif
        }

        private static XmlAttributeOverrides CreateXmlOverrides(Extension[] exs)
        {
#if !SANDBOX
            XmlAttributeOverrides overrides = new XmlAttributeOverrides();
            
            Assembly[] assemblies = new[] { typeof(RootClass).Assembly };

            AddCompositeOverides<List, ListOperation>(assemblies, exs, overrides);
            AddCompositeOverides<ProvisionerRoot, WebOperation>(assemblies, exs, overrides);
            AddCompositeOverides<FixupWebACL, WebACLAction>(assemblies, exs, overrides);
            AddCompositeOverides<WebPart, WebpartOperation>(assemblies, exs, overrides);
            AddCompositeOverides<Web.ActionContainer, WebOperation>(assemblies, exs, overrides);
            return overrides;
#else
            throw new Exception("'CreateXmlOverrides' method cannot be used in sandbox solution");
#endif
        }

        static public RootClass Deserialize(string xmlString)
        {
            using (MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(xmlString)))
            {
                XmlSerializer serializer = new XmlSerializer(typeof(RootClass), CreateXmlOverrides());
                return (RootClass)serializer.Deserialize(stream);
            }
        }

        public static RootClass DeserializeFromFile(string fileName)
        {
            using (FileStream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read))
            {
                XmlSerializer serializer = new XmlSerializer(typeof(RootClass), CreateXmlOverrides());
                return (RootClass)serializer.Deserialize(stream);
            }
        }

        public static RootClass DeserializeFromString(string script, Extension[] exs)
        {
            using (StringReader sr = new StringReader(script))
            {
                var serializer = new XmlSerializer(typeof(RootClass), CreateXmlOverrides(exs));
                return (RootClass)serializer.Deserialize(sr);
            }
        }

        static public RootClass DeserializeFromStringStatic(string script)
        {
            using (StringReader sr = new StringReader(script))
            {
                var serializer = new XmlSerializer(typeof(RootClass));
                return (RootClass)serializer.Deserialize(sr);
            }
        }

        public static RootClass DeserializeFromFile(string fileName, Extension[] exs)
        {
            using (FileStream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read))
            {
                XmlSerializer serializer = new XmlSerializer(typeof(RootClass), CreateXmlOverrides(exs));
                return (RootClass)serializer.Deserialize(stream);
            }
        }

        static public string Serialize(RootClass action)
        {
            using (MemoryStream stream = new MemoryStream())
            {
                XmlSerializer serializer = new XmlSerializer(action.GetType(), CreateXmlOverrides());
                serializer.Serialize(stream, action);
                stream.Position = 0;
                byte[] bytes = new byte[stream.Length];
                stream.Read(bytes, 0, bytes.Length);

                return Encoding.UTF8.GetString(bytes);
            }
        }
        
        public static void SerializeToFile(RootClass data, string fileName)
        {
            using (FileStream stream = new FileStream(fileName, FileMode.Create))
            {
                XmlSerializer serializer = new XmlSerializer(typeof(RootClass), CreateXmlOverrides());
                serializer.Serialize(stream, data);
            }
        }
    }
}
